/****************************************************************************
 * Dirve lcd19264 , mode 8080. Tested on ABG192064C00-v1.0
 * Tested MCU MKE06Z
 * Owner : apleilx
 * 
 * Frequence : 40MHz
 * 
 * Some samble :
 * CN------------Chianese
 * Page----------The Page of LCD
 * Col-----------The col address of LCD
 * 16_16_________A char that is 16*16 dots
 * U-------------The chip select for LCD,(CS1--left / CS2--middle / CS3--right)
 * Com-----------LCD command
 * Disp----------Display
 * W-------------write
 * Rec-----------recently
 * Img-----------Image
 * Anti----------Anti-color
 * DIR-----------direction
 * DEL-----------delay
 * Explanation:
 * ANSEL must be set again
 * This file relay on ASCII_16_32.c,ASCII_8_16
 * that is in pic18f2xk22flash.h .
 * ASCII_16_32.c and ASCII_8_16 is unrelaied on hardware ,you can copy those to your project.
 * Imag.c and CN_16_16_Index.c is not necessary ,unless you want to display chianese and image.
 * **************************************************************************/

#include "system.h"
#include "lcd19264.h"
#include "pincfg.h"

static unsigned char LCDDataMask;
static FontType FontCur;
unsigned char const * AsciiImg;
static unsigned char FontSize;
static unsigned char bHalfSpace;
static unsigned char UpdateCnt;

unsigned char LcdRam[LCD_LINE_SIZE * LCD_PAGE_SIZE];

/******************************************************************************
 * Init
 * @param Content
 *****************************************************************************/
void LcdInit() {
    LcdHalInit();
    bHalfSpace = 1;
    LCDDataMask = 0x00;

    LcdDispAnti();
    LcdDispNormal();
    LcdDispHalfDis();
    LcdDispHalfEn();

    LcdFontSet(FONT16);
}

/******************************************************************************
 * Lcd Font Set
 * @param Content
 *****************************************************************************/
void LcdFontSet(FontType Font) {
    FontCur = Font;

    switch (Font) {
        case FONT16:
            AsciiImg = &ASCII_SongTi_8_16[0][0];
            FontSize = 16 * 2;
            break;
        case FONT24:
            AsciiImg = &ASCII_SongTi_12_24[0][0];
            FontSize = 24 * 3;
            break;
        case FONT32:
            AsciiImg = &ASCII_SongTi_16_32[0][0];
            FontSize = 32 * 4;
            break;
        default:
            AsciiImg = &ASCII_SongTi_8_16[0][0];
            FontCur = FONT16;
            FontSize = 16 * 2;
            break;

    }
}

/******************************************************************************
 * DispAnti
 * @param Content
 *****************************************************************************/
void LcdDispAnti() {
    LCDDataMask = 0xFF;
}

void LcdDispHalfEn(void) {
    bHalfSpace = 1;
}

void LcdDispHalfDis(void) {
    bHalfSpace = 0;
}

/******************************************************************************
 * Disp Normal
 * @param Content
 *****************************************************************************/
void LcdDispNormal() {
    LCDDataMask = 0x00;
}

void LcdPutCh(unsigned char Page, unsigned char Col, unsigned char DispCh) {
    unsigned char LMR; // left /middle /right screen
    unsigned char ColCounter;
    unsigned char FontLineSize;
    unsigned char LMRChange;
    unsigned char PageIndex;

    FontLineSize = (FontSize / FontCur) >> 1;
    if (Page > (8 - FontCur)) return;
    if (Col > (LCD_LINE_SIZE - FontLineSize)) return;

    LMR = Col >> 6;
    ColCounter = 0;
    LMRChange = 0;

    PageIndex = 0;
    while (PageIndex < FontCur) {

        Lcd_WriteCom(LMR, 0xB8 + Page + PageIndex); // Set the first Page of the char
        Lcd_WriteCom(LMR, 0x40 + (Col & 0x3F)); // Set the start Col of the char
        LMRChange = 0;
        while (ColCounter < FontLineSize) //Write the first page of the char
        {
            if ((Col + ColCounter) >= (64 * (LMR + 1))) {
                if (LMRChange == 0) {
                    LMRChange = 1;
                    Lcd_WriteCom(LMR + 1, 0xB8 + Page + PageIndex); // Set the first Page of the char
                    Lcd_WriteCom(LMR + 1, 0x40); // Set the start Col of the char
                }
            }

            Lcd_DataW(AsciiImg[((DispCh - 0x20) * (FontSize >> 1)) + (PageIndex * FontLineSize + (ColCounter++))] ^ LCDDataMask);
        }
        PageIndex++;
        ColCounter = 0;
    }
}

/******************************************************************************
 * Fnally,this is just a simple function that does not support formatted output.
 * @param Content
 *****************************************************************************/
void LcdPuts(unsigned char Page, unsigned char Col, unsigned char *DispStr) {
    while (*DispStr > 0x19) {
        LcdPutCh(Page, Col, *DispStr);

        if (bHalfSpace && ('.' == *DispStr))
            Col += (FontSize / FontCur) >> 2;
        else
            Col += (FontSize / FontCur) >> 1;

        if (Col > LCD_LINE_SIZE)
            break;

        DispStr++;
    }
}

/******************************************************************************
 * Fnally,this is just a simple function that does not support formatted output.
 * @param Content
 *****************************************************************************/
void LcdPutCN(unsigned char Page, unsigned char Col, unsigned char const *pCN) {
    unsigned char LMR; // left /middle /right screen
    unsigned char ColCounter;
    unsigned char FontLineSize;
    unsigned char LMRChange;
    unsigned char PageIndex;

    FontLineSize = FontSize / FontCur;
    if (Page > (8 - FontCur)) return;
    if (Col > (LCD_LINE_SIZE - FontLineSize)) return;

    LMR = Col >> 6;
    ColCounter = 0;
    LMRChange = 0;

    PageIndex = 0;
    while (PageIndex < FontCur) {
        Lcd_WriteCom(LMR, 0xB8 + Page + PageIndex); // Set the first Page of the char
        Lcd_WriteCom(LMR, 0x40 + (Col & 0x3F)); // Set the start Col of the char
        LMRChange = 0;

        while (ColCounter < FontLineSize) //Write the first page of the char
        {
            if ((Col + ColCounter) >= (64 * (LMR + 1))) {
                if (LMRChange == 0) {
                    LMRChange = 1;
                    Lcd_WriteCom(LMR + 1, 0xB8 + Page + PageIndex); // Set the first Page of the char
                    Lcd_WriteCom(LMR + 1, 0x40); // Set the start Col of the char
                }
            }
            Lcd_DataW(pCN[PageIndex * FontLineSize + (ColCounter++)] ^ LCDDataMask);
        }
        PageIndex++;
        ColCounter = 0;
    }
}

/******************************************************************************
 * Fnally,this is just a simple function that does not support formatted output.
 * @param Content
 *****************************************************************************/
void LcdPutCNs(unsigned char Page, unsigned char Col, unsigned char Amount, unsigned char const *pCN) {
    while (Amount--) {
        LcdPutCN(Page, Col, pCN);
        pCN += FontSize;
        Col += (FontSize / FontCur);
        if (Col > LCD_LINE_SIZE)
            break;
    }
}

/******************************************************************************
 * This function used to display image in one line
 *
 * @param Content
 *****************************************************************************/
void LcdLineDisp(unsigned char Page, unsigned char Col, unsigned char Wide, unsigned char const *pImg) {
    unsigned char StartLMR, RecLMR;
    unsigned char ColRel;
    unsigned char WideUnprinted;
    unsigned char Index;


    StartLMR = Col >> 6;
    ColRel = Col & 0x3F;

    RecLMR = StartLMR;
    WideUnprinted = Wide;
    Index = 0;
    /************************************Wriet Data****************************/
    while (RecLMR < LCD_CS) //Screen is not over
    {
        Lcd_WriteCom(RecLMR, 0xB8 + Page); //Set Page
        Lcd_WriteCom(RecLMR, 0x40 + ColRel); //Set Start Col

        while ((ColRel++ < 64)) {
            Lcd_DataW(pImg[Index++] ^ LCDDataMask);
            if ((--WideUnprinted) == 0) return;
        }
        RecLMR++;
        ColRel = 0;
    }
    /*************************************************************************/
}

/****************************************************************************
 * The High must be a multiple of 8.
 ****************************************************************************//*
void LcdImgDisp(unsigned char StartPage ,unsigned char StartCol ,\
unsigned char Wide ,unsigned char PageHigh ,unsigned char const *pImg)
{
    while( (PageHigh > 0 ) && (StartPage < 8) )
    {
        LcdLineDisp(StartPage,StartCol,Wide,pImg);
        pImg += Wide;
        PageHigh --;
        StartPage ++;
    }
	Lcd_CS2_Disable();
	Lcd_CS3_Disable();
	Lcd_CS1_Disable();
}
*/

/******************************************************************************
 * This function used to display image in one line
 *
 * @param Content
 *****************************************************************************/
void LcdPoll() {
    uint8_t *pData;
    uint8_t Page;
    uint8_t LMR;
    uint8_t ColCnt;

    LMR = UpdateCnt >> 3;
    Page = UpdateCnt & 0x07;

    pData = &LcdRam[(uint16_t) Page * LCD_LINE_SIZE + (uint16_t) LMR * LCD_LINE_SECTOR];

    Lcd_WriteCom(LMR, 0xB8 + Page); //Set Page
    Lcd_WriteCom(LMR, 0x40 + 0); //Set Start Col

    ColCnt = 0;
    while ((ColCnt++ < LCD_LINE_SECTOR)) {
        Lcd_DataW(*pData++);
    }

    UpdateCnt++;
    if (UpdateCnt >= LCD_CS * LCD_PAGE_SIZE)
        UpdateCnt = 0;
}

/******************************************************************************
 * This function used to display image in one line
 *
 * @param Content
 *****************************************************************************/
void PutCh(unsigned char Page, unsigned char Col, unsigned char DispCh) {
    unsigned char ColOffset;
    unsigned char FontLineSize;
    unsigned char PageOffset;
    unsigned char *pRam;
    const unsigned char *pRom;

    FontLineSize = (FontSize / FontCur) >> 1;
    if (Page >= LCD_PAGE_SIZE) return;
    if (Col >= LCD_LINE_SIZE) return;

    PageOffset = 0;
    while ((PageOffset < FontCur) && (Page < LCD_PAGE_SIZE)) {
        ColOffset = 0;
        pRam = &LcdRam[(uint16_t) Page * LCD_LINE_SIZE + Col];
        pRom = &AsciiImg[((DispCh - 0x20) * (FontSize >> 1)) +
                (PageOffset * FontLineSize + (ColOffset))];
        while ((ColOffset < FontLineSize) && ((ColOffset + Col) < LCD_LINE_SIZE)) //Write the first page of the char
        {
            *pRam++ = (*pRom++) ^ LCDDataMask;
            ColOffset++;
        }
        PageOffset++;
        Page++;
    }
}

/******************************************************************************
 * This function used to display image in one line
 *
 * @param Content
 *****************************************************************************/
void Puts(unsigned char Page, unsigned char Col, unsigned char *DispStr) {
    while (*DispStr > 0x19) {
        PutCh(Page, Col, *DispStr);

        if (bHalfSpace && ('.' == *DispStr))
            Col += (FontSize / FontCur) >> 2;
        else
            Col += (FontSize / FontCur) >> 1;

        if (Col > LCD_LINE_SIZE)
            break;

        DispStr++;
    }
}

/******************************************************************************
 * This function used to display image in one line
 *
 * @param Content
 *****************************************************************************/
void PutCn(unsigned char Page, unsigned char Col, unsigned char const *pCN) {
    unsigned char ColOffset;
    unsigned char FontLineSize;
    unsigned char PageOffset;
    unsigned char *pRam;
    const unsigned char *pRom;

    FontLineSize = (FontSize / FontCur);
    if (Page >= LCD_PAGE_SIZE) return;
    if (Col >= LCD_LINE_SIZE) return;

    PageOffset = 0;
    while ((PageOffset < FontCur) && (Page < LCD_PAGE_SIZE)) {
        ColOffset = 0;
        pRam = &LcdRam[(uint16_t) Page * LCD_LINE_SIZE + Col];
        pRom = pCN + FontLineSize * PageOffset;
        while ((ColOffset < FontLineSize) && ((ColOffset + Col) < LCD_LINE_SIZE)) //Write the first page of the char
        {
            *pRam++ = (*pRom++) ^ LCDDataMask;
            ColOffset++;
        }
        PageOffset++;
        Page++;
    }
}

/******************************************************************************
 * This function used to display image in one line
 *
 * @param Content
 *****************************************************************************/
void PutCns(unsigned char Page, unsigned char Col, unsigned char Amount, unsigned char const *pCN) {
    uint8_t FontLineSize;

    while (Amount--) {
        PutCn(Page, Col, pCN);
        pCN += FontSize;
        FontLineSize = FontSize / FontCur;
        Col += FontLineSize;

        if (Col >= LCD_LINE_SIZE + FontLineSize)
            break;
    }
}

/******************************************************************************
 * This function used to display image in one line
 *
 * @param Content
 *****************************************************************************/
void LcdRamFill(uint8_t Val) {
    uint16_t Index;

    Index = LCD_LINE_SIZE * LCD_PAGE_SIZE;

    while (Index--)
        LcdRam[Index] = Val;

}